package com.net.udpTest;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class Client {
    private static final int PORT = 7877;
    private static final String SERVER_IP = "127.0.0.1"; // 服务器IP地址

    public static void main(String[] args) throws IOException {
        InetAddress serverAddress = InetAddress.getByName(SERVER_IP);
        DatagramSocket clientSocket = new DatagramSocket();

        String message = "Hello, Server!sdlnogeirjojalngerijopiwm;bkpjaoijer;kljo" +
                "sdjfpowihjgorjgpoejj;alkjgopirehgp9erouijgoewijgoerge';lwg end@!!!!!!!!!!!";
        byte[] sendData = message.getBytes();

        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, PORT);
        clientSocket.send(sendPacket);
        String message2 = "Hello, Server!sdlnogeirjojalngerijopiwm;bkpjaoijer;kljo" +
                "sdjfpowihjgorjgpoejj;alkjgopirehgp9erouijgoewijgoerge';lwg end@!!!!!!!" +
                "sdiujhp2iuyh98df;igjh9po38rtgu-0[32q4tfijw4p'4goj5309i-2r!!!!osidjfodsf" +
                "2222" +
                "class BeatTask implements Runnable {        \n" +
                "        BeatInfo beatInfo; \n" +
                "        public BeatTask(BeatInfo beatInfo) {\n" +
                "            this.beatInfo = beatInfo;\n" +
                "        }        \n" +
                "        @Override\n" +
                "        public void run() {\n" +
                "            //终止定时任务的一个思路，一个标记位\n" +
                "            if (beatInfo.isStopped()) {\n" +
                "                return;\n" +
                "            }\n" +
                "            //得到延迟时间，下面的逻辑可以更改此时间。\n" +
                "            long nextTime = beatInfo.getPeriod();\n" +
                "           \t//中间省略...........\n" +
                "             finally {\n" +
                "                //再构造一个心跳延迟任务\n" +
                "                //这是定时任务的一个良好思路：反复的添加延迟任务。\n" +
                "                executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);\n" +
                "            }\n" +
                "        }\n" +
                "    }" + "> 你大概说说Nacos中服务的注册流程\n" +
                ">\n" +
                "> 你再说说服务发现流程\n" +
                ">\n" +
                "> 再说说如何实现心跳监测的\n" +
                ">\n" +
                "> 说说Nacos作为注册中心，是如何实现动态感知服务上线的？\n" +
                ">\n" +
                "> 其他服务如何直到下线\n" +
                "\n" +
                "Nacos运行的一些信息在文件位置在  C:\\Users\\moutainman\\nacos\\logs\n" +
                "C:\\Users\\moutainman\\nacos\\conf\n" +
                "C:\\Users\\moutainman\\nacos\\data，记得清除\n" +
                "\n"
                + "于周期任务，naocs client使用的是JDK自带的 `ScheduledExecutorService`，然后延迟5s执行心跳发送。 细心的你一定发现，<font color='red'>由于是延迟5s执行心跳发送，而非周期5s，这明显是一个延迟任务啊，只会到规定的时间延迟执行一次</font>，何来的定时心跳发送？\n" +
                "\n" +
                "不急， BeatTask这个类实现Runnable接口，看一下源码，我截取了核心：\n" +
                "\n" +
                "```java\n" +
                "class BeatTask implements Runnable {        \n" +
                "        BeatInfo beatInfo; \n" +
                "        public BeatTask(BeatInfo beatInfo) {\n" +
                "            this.beatInfo = beatInfo;\n" +
                "        }        \n" +
                "        @Override\n" +
                "        public void run() {\n" +
                "            //终止定时任务的一个思路，一个标记位\n" +
                "            if (beatInfo.isStopped()) {\n" +
                "                return;\n" +
                "            }\n" +
                "            //得到延迟时间，下面的逻辑可以更改此时间。\n" +
                "            long nextTime = beatInfo.getPeriod();\n" +
                "           \t//中间省略...........\n" +
                "             finally {\n" +
                "                //再构造一个心跳延迟任务\n" +
                "                //这是定时任务的一个良好思路：反复的添加延迟任务。\n" +
                "                executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);\n" +
                "            }\n" +
                "        }\n" +
                "    }\n" +
                "```\n" +
                "\n" +
                "刚才的问题真相大白，在run方法的末尾，重新调用一次`executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);`， \n" +
                "\n" +
                "关于 `BeatTask` 我有两点思考\n" +
                "\n" +
                "> #### 一、\n" +
                ">\n" +
                "> nacos这种定时任务的实现值得学习，\n" +
                ">\n" +
                "> 一句话讲明白：当前任务结束后，再向线程池添加一个延迟任务。通过不停的添加延迟任务实现定时任务的方式，由于延迟的时间我们可以随意更改。 这样就可以根据系统运行情况 ，改变定时任务的周期，达到定时任务周期变频的效果。很多中间件都是如此\n" +
                ">\n" +
                "> #### 二、\n" +
                ">\n" +
                "> 如何终止定时任务？\n" +
                ">\n" +
                "> run()方法第一句给了答案，beatInfo里面有一个 boolean变量 stop，为true表示任务终止，run()方法直接退出了。\n" +
                ">\n" +
                "> ```java\n" +
                "> //终止定时任务的一个思路，一个标记位\n" +
                "> if (beatInfo.isStopped()) {\n" +
                ">   return;\n" +
                "> }\n" +
                "> ```\n" +
                ">\n" +
                "> nacos 把 serveiceName + ip +port作为key，beatInfo作为value，用map维持映射关系，这样就通过map获取到 beatInfo，将BeatInfo的stop 变量设置为true，终止定时任务了。\n" +
                "\n" +
                "\n" +
                "\n" +
                "##### client心跳发送失败？500了咋办？\n" +
                "\n" +
                "`Nacos server`是集群部署，各个节点的信息会互相传递。client也是随机选择一个nacos server发送心跳，如果发送失败会再选一个server节点发送心跳。\n" +
                "\n" +
                "如果都失败，再次随机选择一个server节点，发起注册。<u>因为心跳发送失败，可能网络阻塞原因，心跳传输太慢。nacos server对应的client 心跳超时了</u>，所以需要client要重新向server注册。\n" +
                "\n" +
                "#### 向nacos服务端 发送http 服务注册请求\n" +
                "\n" +
                "```\n" +
                "/nacos/v1/ns/instance\n" +
                "```\n" +
                "\n" +
                "\n" +
                "\n" +
                "## 2、Nacos Server如何处理Client的注册请求 1.x\n" +
                "\n" +
                "处理的话逻辑较为简单，\n" +
                "\n" +
                "首先从请求中解析出 Instance，**并设置当前实例的最后一次心跳时间为 当前时间**。\n" +
                "\n" +
                "接着创建Service，每一个service都会开启 心跳检查任务\n" +
                "\n" +
                "> 一个普通的定时任务，默认5s检查当前service的所有实例，如果超过15s，则认为不健康，同时向其他服务推送状态；如果超过30s，则直接从注册表里移除。\n" +
                "\n" +
                "\n" +
                "\n" +
                "将当前实例Intance添加到Service的Intance集合中。  key为服务名，value为Service（包含Instance的对象）\n" +
                "\n" +
                "最后发布服务的实例注册事件。也是udp推送\n" +
                "\n" +
                "\n" +
                "\n" +
                "## 3、服务发现和订阅的过程 ---1.x\n" +
                "\n" +
                "HostReactor，位于`com.alibaba.nacos.client.naming.core`\n" +
                "\n" +
                "client启动时，向Nacos发送http服务拉取请求，参数为serveviceName，udp端口等。并把服务实例信息放到本地得缓存里。\n" +
                "\n" +
                "#### 同时开启定时任务，不停的拉取所有的实例信息 ，并更新本地缓存；\n" +
                "\n" +
                "\n" +
                "\n" +
                "\n" +
                "\n" +
                "#### 接收udp推送，服务状态变更。\n" +
                "\n" +
                "\n" +
                "\n" +
                "##### Nacos服务状态的变更为啥要用 UDP推送？\n" +
                "\n" +
                "无连接，节约资源。推送速度快，\n" +
                "\n" +
                "\n" +
                "\n" +
                "服务订阅的过程，可以通过日志查看实例是否发生改变，源码位于`HostReactor`的`public ServiceInfo processServiceJson(String json)`方法";



        byte[] bytes = message2.getBytes(StandardCharsets.UTF_8);
        String message23 = "Hello, Server!sdlnogeirjojalngerijopiwm;bkpjaoijer;kljosdjfpowihjgorjgpoejj;alkjgopirehgp9erouijgoewijgoerge';lwg end@!!!!!!!sdiujhp2iuyh98df;igjh9po38rtgu-0[32q4tfijw4p'4goj5309i-2r!!!!osidjfodsf2222class BeatTask implements Runnable {        \n" +
                "        BeatInfo beatInfo; \n" +
                "        public BeatTask(BeatInfo beatInfo) {\n" +
                "            this.beatInfo = beatInfo;\n" +
                "        }        \n" +
                "        @Override\n" +
                "        public void run() {\n" +
                "            //终止定时任务的一个思路，一个标记位\n" +
                "            if (beatInfo.isStopped()) {\n" +
                "                return;\n" +
                "            }\n" +
                "            //得到延迟时间，下面的逻辑可以更改此时间。\n" +
                "            long nextTime = beatInfo.getPeriod();\n" +
                "           \t//中间省略...........\n" +
                "             finally {\n" +
                "                //再构造一个心跳延迟任务\n" +
                "                //这是定时任务的一个良好思路：反复的添加延迟任务。\n" +
                "                executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);\n" +
                "            }\n" +
                "        }\n" +
                "    }> 你大概说说Nacos中服务的注册流程\n" +
                ">\n" +
                "> 你再说说服务发现流程\n" +
                ">\n" +
                "> 再说说如何实现心跳监测的\n" +
                ">\n" +
                "> 说说Nacos作为注册中心，是如何实现动态感知服务上线的？\n" +
                ">\n" +
                "> 其他服务如何直到下线\n" +
                "\n" +
                "Nacos运行的一些信息在文件位置在  C:\\Users\\moutainman\\nacos\\logs\n" +
                "C:\\Users\\moutainman\\nacos\\conf\n" +
                "C:\\Users\\moutainman\\nacos\\data，记得清除\n" +
                "\n" +
                "于周期任务，naocs client使用的是JDK自带的 `ScheduledExecutorService`，然后延迟5s执行心跳发送。 细心的你一定发现，<font color='red'>由于是延迟5s执行心跳发送，而非周期5s，这明显是一个延迟任务啊，只会到规定的时间延迟执行一次</font>，何来的定时心跳发送？\n" +
                "\n" +
                "不急， BeatTask这个类实现Runnable接口，看一下源码，我截取了核心：\n" +
                "\n" +
                "```java\n" +
                "class BeatTask implements Runnable {        \n" +
                "        BeatInfo beatInfo; \n" +
                "        public BeatTask(BeatInfo beatInfo) {\n" +
                "            this.beatInfo = beatInfo;\n" +
                "        }        \n" +
                "        @Override\n" +
                "        public void run() {\n" +
                "            //终止定时任务的一个思路，一个标记位\n" +
                "            if (beatInfo.isStopped()) {\n" +
                "                return;\n" +
                "            }\n" +
                "            //得到延迟时间，下面的逻辑可以更改此时间。\n" +
                "            long nextTime = beatInfo.getPeriod();\n" +
                "           \t//中间省略...........\n" +
                "             finally {\n" +
                "                //再构造一个心跳延迟任务\n" +
                "                //这是定时任务的一个良好思路：反复的添加延迟任务。\n" +
                "                executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);\n" +
                "            }\n" +
                "        }\n" +
                "    }\n" +
                "```\n" +
                "\n" +
                "刚才的问题真相大白，在run方法的末尾，重新调用一次`executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);`， \n" +
                "\n" +
                "关于 `BeatTask` 我有两点思考\n" +
                "\n" +
                "> #### 一、\n" +
                ">\n" +
                "> nacos这种定时任务的实现值得学习，\n" +
                ">\n" +
                "> 一句话讲明白：当前任务结束后，再向线程池添加一个延迟任务。通过不停的添加延迟任务实现定时任务的方式，由于延迟的时间我们可以随意更改。 这样就可以根据系统运行情况 ，改变定时任务的周期，达到定时任务周期变频的效果。很多中间件都是如此\n" +
                ">\n" +
                "> #### 二、\n" +
                ">\n" +
                "> 如何终止定时任务？\n" +
                ">\n" +
                "> run()方法第一句给了答案，beatInfo里面有一个 boolean变量 stop，为true表示任务终止，run()方法直接退出了。\n" +
                ">\n" +
                "> ```java\n" +
                "> //终止定时任务的一个思路，一个标记位\n" +
                "> if (beatInfo.isStopped()) {\n" +
                ">   return;\n" +
                "> }\n" +
                "> ```\n" +
                ">\n" +
                "> nacos 把 serveiceName + ip +port作为key，beatInfo作为value，用map维持映射关系，这样就通过map获取到 beatInfo，将BeatInfo的stop 变量设置为true，终止定时任务了。\n" +
                "\n" +
                "\n" +
                "\n" +
                "##### client心跳发送失败？500了咋办？\n" +
                "\n" +
                "`Nacos server`是集群部署，各个节点的信息会互相传递。client也是随机选择一个nacos server发送心跳，如果发送失败会再选一个server节点发送心跳。\n" +
                "\n" +
                "如果都失败，再次随机选择一个server节点，发起注册。<u>因为心跳发送失败，可能网络阻塞原因，心跳传输太慢。nacos server对应的client 心跳超时了</u>，所以需要client要重新向server注册。\n" +
                "\n" +
                "#### 向nacos服务端 发送http 服务注册请求\n" +
                "\n" +
                "```\n" +
                "/nacos/v1/ns/instance\n" +
                "```\n" +
                "\n" +
                "\n" +
                "\n" +
                "## 2、Nacos Server如何处理Client的注册请求 1.x\n" +
                "\n" +
                "处理的话逻辑较为简单，\n" +
                "\n" +
                "首先从请求中解析出 Instance，**并设置当前实例的最后一次心跳时间为 当前时间**。\n" +
                "\n" +
                "接着创建Service，每一个service都会开启 心跳检查任务\n" +
                "\n" +
                "> 一个普通的定时任务，默认5s检查当前service的所有实例，如果超过15s，则认为不健康，同时向其他服务推送状态；如果超过30s，则直接从注册表里移除。\n" +
                "\n" +
                "\n" +
                "\n" +
                "将当前实例Intance添加到Service的Intance集合中。  key为服务名，value为Service（包含Instance的对象）\n" +
                "\n" +
                "最后发布服务的实例注册事件。也是udp推送\n" +
                "\n" +
                "\n" +
                "\n" +
                "## 3、服务发现和订阅的过程 ---1.x\n" +
                "\n" +
                "HostReactor，位于`com.alibaba.nacos.client.naming.core`\n" +
                "\n" +
                "client启动时，向Nacos发送http服务拉取请求，参数为serveviceName，udp端口等。并把服务实例信息放到本地得缓存里。\n" +
                "\n" +
                "#### 同时开启定时任务，不停的拉取所有的实例信息 ，并更新本地缓存；\n" +
                "\n" +
                "\n" +
                "\n" +
                "\n" +
                "\n" +
                "#### 接收udp推送，服务状态变更。\n" +
                "\n" +
                "\n" +
                "\n" +
                "##### Nacos服务状态的变更为啥要用 UDP推送？\n" +
                "\n" +
                "无连接，节约资源。推送速度快，\n" +
                "\n" +
                "\n" +
                "\n" +
                "服务订阅的过程，可以通过日志查看实例是否发生改变，源码位于`HostReactor`的`public ServiceInfo processServiceJson(String json)`方法";

        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, serverAddress, PORT);

        clientSocket.send(datagramPacket);

        // 接收响应
        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
        clientSocket.receive(receivePacket);
        String receivedMessage = new String(receivePacket.getData()).trim();
        System.out.println("Received: " + receivedMessage);

        clientSocket.close();
    }
}